package top.mydb.hejianwei.tm;
import java.io.File; // Import File
import java.io.FileNotFoundException; // Import FileNotFoundException
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import top.mydb.hejianwei.common.Error;
import top.mydb.hejianwei.utils.Panic;
import top.mydb.hejianwei.utils.Parser;

public class TransactionManagerImpl implements TransactionManager {
    //XID文件长度8字节，存储事务计数器
    static final int LEN_XID_HEADER_LENGTH = 8;
    //每个事务占用1字节
    static final int XID_FIELD_SIZE = 1;

    //事务的三种状态
    private static final byte FIELD_TRAN_ACTIVE = 0; // 事务正在进行
    private static final byte FIELD_TRAN_COMMITTED = 1; // 事务已提交
    private static final byte FIELD_TRAN_ABORTED = 2; // 事务已取消

    //超级事务，永远为0 权限高
    public static final long SUPER_XID = 0;

    //.xid文件后缀名
    static final String XID_FILE_SUFFIX = ".xid";

    private RandomAccessFile file;
    private FileChannel fc;
    private long xidCounter; // 事务计数器
    private Lock counterLock; // 用于同步访问

    // [REVISED] The constructor now takes a path and handles file opening itself.
    public TransactionManagerImpl(String path) {
        this.counterLock = new ReentrantLock();
        File f = new File(path + XID_FILE_SUFFIX);
        try {
            this.file = new RandomAccessFile(f, "rw");
            this.fc = this.file.getChannel();
        } catch (FileNotFoundException e) {
            Panic.panic(e);
        }
        checkXIDCounter();
    }

    // [REMOVED] The old constructor is no longer needed.
    // public TransactionManagerImpl(RandomAccessFile file, FileChannel fc) { ... }

    private void checkXIDCounter(){
        long fileLen = 0;
        try {
            fileLen = file.length();
        } catch (IOException e) {
            Panic.panic(Error.BadXIDFileException);
        }
        if(fileLen < LEN_XID_HEADER_LENGTH){
            Panic.panic(Error.BadXIDFileException);
        }

        ByteBuffer buffer = ByteBuffer.allocate(LEN_XID_HEADER_LENGTH);
        try {
            fc.read(buffer, 0);
        } catch (IOException e) {
            Panic.panic(e);
        }
        this.xidCounter = Parser.bytes2Long(buffer.array());

        long expectedFileLen = LEN_XID_HEADER_LENGTH + this.xidCounter * XID_FIELD_SIZE;
        if(expectedFileLen != fileLen) {
            Panic.panic(Error.BadXIDFileException);
        }

        if(this.xidCounter < 0){
            Panic.panic(Error.BadXIDFileException);
        }
    }


    private long getXidPosition(long xid){
        return LEN_XID_HEADER_LENGTH + (xid - 1) * XID_FIELD_SIZE;
    }

    private void updateXID(long xid,byte status){
        long offset = getXidPosition(xid);
        byte[] data = new byte[XID_FIELD_SIZE];
        data[0] = status;
        ByteBuffer buffer = ByteBuffer.wrap(data);
        try {
            fc.write(buffer, offset);
        } catch (IOException e) {
            Panic.panic(e);
        }
        try {
            fc.force(false);
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    private void incrXIDCounter() {
        counterLock.lock();
        try {
            xidCounter++;
            ByteBuffer buf = ByteBuffer.wrap(Parser.long2Bytes(xidCounter));
            fc.position(0);
            fc.write(buf);
            fc.force(false);
        } catch (IOException e) {
            Panic.panic(e);
        } finally {
            counterLock.unlock();
        }
    }

    @Override
    public long begin() {
        counterLock.lock();
        try {
            long xid = xidCounter + 1;
            updateXID(xid, FIELD_TRAN_ACTIVE);
            incrXIDCounter();
            return xid;
        } finally {
            counterLock.unlock();
        }
    }
    @Override
    public void commit(long xid) {
        updateXID(xid, FIELD_TRAN_COMMITTED);
    }

    @Override
    public void abort(long xid) {
        updateXID(xid, FIELD_TRAN_ABORTED);
    }

    private boolean checkXID(long xid, byte status) {
        if (xid < 1 || xid > xidCounter) {
            return false;
        }
        long offset = getXidPosition(xid);
        ByteBuffer buffer = ByteBuffer.allocate(XID_FIELD_SIZE);
        try {
            fc.read(buffer, offset);
        } catch (IOException e) {
            Panic.panic(e);
        }
        return buffer.get(0) == status;
    }
    @Override
    public boolean isActive(long xid) {
        return checkXID(xid, FIELD_TRAN_ACTIVE);
    }
    @Override
    public boolean isCommitted(long xid) {
        return checkXID(xid, FIELD_TRAN_COMMITTED);
    }
    @Override
    public boolean isAborted(long xid) {
        return checkXID(xid, FIELD_TRAN_ABORTED);
    }

    @Override
    public void close() {
        try {
            fc.close();
            file.close();
        } catch (IOException e) {
            Panic.panic(e);
        }
    }
}